# coding=utf-8

##############################################################################
#
# simpleprocessor.py
# A SimpleProcessor class
#
# Copyright (c) 2007 Webcore Corp. All Rights Reserved.
#
##############################################################################
""" simpleprocessor.py - A Processor class to pass new items to publisher

$Id: simpleprocessor.py 17 2011-09-27 07:22:33Z shib.ats $
"""

__author__  = 'Atsushi Shibata <shibata@webcore.co.jp>'
__docformat__ = 'plaintext'
__licence__ = 'MIT'

import logging
from datetime import datetime

from pluginbase import BaseProcessor

class SimpleProcessor(BaseProcessor):
    """
    Simple Processor
    新着itemをpublisherに渡すシンプルなProcessor
    """
    
    # 処理済みのIDを保存する数
    max_ids = 10000
    
    def __init__(self, setting_d, feeders, publishers):
        """
        設定を読み込んで初期化する
        設定はシーケンスで，一要素が以下の形式になっており
        publisherとfeederの関係を記述する
        conditionには，Pythonの式を文字列として記述し
        feederのitemをpublisherでハンドリングするかどうかを判別するために使う
        ex. item['link'].startswith('http://www.python.org')
        
        publisherのID, {'feeders':[feederのid, ...],
                        'condition':"Pythonの式(オプション)"}
        """
        self._publishers = []
        self._feeders = {}
        # 設定が正しいかどうか調べながら追加してゆく
        for k in setting_d.keys():
            if k not in ['classname', 'id']:
                if k not in [x for x in publishers]:
                    raise KeyError("""Publishers for ID '%s' not found""" % k)
                for feeder_id in setting_d[k]['feeders']:
                    if feeder_id not in [x for x in feeders]:
                        raise KeyError("""Feeder for ID '%s' not found""" % \
                                                                    feeder_id)
                    self._feeders[feeder_id] = 1
                self._publishers.append( (k, setting_d[k]) )

    def process(self, settings, feedmgr, publishmgr):
        """
        設定にしたがってfeederを呼び出し，itemをpublisherに渡してゆく
        """
        self._make_ids(settings)
        # conditionを評価しつつ，新着アイテムをpublisherに渡してゆく
        new_items = self._feed_all(settings, feedmgr)
        for pub in self._publishers:
            cond = pub[1].get('condition', 'True')
            for feeder in pub[1]['feeders']:
                for item in new_items[feeder]:
                    if eval(cond):
                        publishmgr.publish(pub[0], item)
            publishmgr.finish_publish(pub[0])
        self._id_list = self._id_list[:self.max_ids]
        settings['ids'] = self._id_list

    def _feed_all(self, settings, feedmgr):
        """
        Processorに登録済みのfeederすべてについてfeedを呼び出し，新着itemのみを返す
        新着itemはfeederのIDをキーに持つ辞書にリストとして登録する
        """
        item_d = {}
        #まずは必要なfeederのfeed()をすべて呼び，新着itemを抜き出して辞書に登録する
        for k in self._feeders.keys():
            items = feedmgr.feed(k)
            unique_items = [x for x in items if self._is_unique(x)]
            self._id_list = [x['id'] for x in unique_items]+self._id_list
            item_d[k] = unique_items
        return item_d

    def _is_unique(self, item):
        """
        itemが新着かどうかをidを使って判別，結果を返す
        """
        if self._id_dict.has_key(item['id']):
            return False
        self._id_dict[item['id']]=1
        return True

    def _make_ids(self, settings):
        """
        shelve(settings)から処理済みのIDリストを生成する
        """
        self._id_dict = {}
        d = self._id_dict
        if not settings.has_key('ids'):
            settings['ids'] = []
        self._id_list = settings['ids']
        [ d.update({x:1}) for x in settings['ids'] ]
